/* Copyright (C) 2005, Levin Du
 * Author:  Levin Du <zsdjw@21cn.com>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANT; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public LIcense for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <errno.h>
#include <kudzu/kudzu.h>
#include <newt.h>
#include "magicbooter.h"
#include "utils.h"
#include "mblog.h"
#include "newtprog.h"

#define PRINTF(fmt, args...) \
    newtProgressWindowPrint(prog_win, fmt, ##args)

#define CONTPRINTF(fmt, args...) \
    newtProgressWindowCont(prog_win, fmt, ##args)

#define STEPIT(march_step)                              \
    do {                                                \
        newtProgressWindowStep(prog_win, march_step);   \
        steps_left -= march_step;                       \
    } while(0)


void detect_scsi(newtProgressWindow prog_win, int steps);

const char * detect_hardware_runner(void) {
    int i;
    newtProgressWindow prog_win;

    struct {
        void (*func)(newtProgressWindow prog_win, int steps);
        int steps;
    } table[] = {               /* steps should sum up to 100 */
        {detect_scsi, 100},
    };

    prog_win = newtProgressWindowOpen(52, 5, "Initial Hardware");
    for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) {
        table[i].func(prog_win, table[i].steps);
        sleep(1);
    }
    newtProgressWindowClose(prog_win);

    return NULL;
}

void detect_scsi(newtProgressWindow prog_win, int steps)
{
    int steps_left = steps;
    struct device ** devices, ** device;
    char *driver;

    PRINTF("Probing SCSI device...");

    devices = probeDevices(CLASS_SCSI, BUS_PCI, PROBE_ALL);

    STEPIT(steps / 2);
    steps -= steps / 2;
    if (devices != NULL) {
        int cnt = 0;

        for (device = devices; *device; device++)
            cnt++;

        for (device = devices; *device; device++) {
            driver = (*device)->driver;
            if (! driver)
                continue;
            mblog("SCSI device with driver '%s'.\n", driver);
            if (strcmp(driver, "ignore")
                && strcmp(driver, "unknown")
                && strcmp(driver, "disabled")) {
                PRINTF("Loading module '%s'...", driver);
                if (do_modprobe(driver) >= 0)
                    CONTPRINTF("[OK]");
                else
                    CONTPRINTF("[Failed]");
            }
            freeDevice (*device);

            STEPIT(steps / cnt);
        }
        free(devices);
    }
    else
        CONTPRINTF("[None]");

    if (steps_left > 0)
        STEPIT(steps_left);
}

#ifdef USE_UDEV
void start_udev(newtProgressWindow prog_win, int steps);

const char * start_udev_runner(void) {
    newtProgressWindow prog_win;

    prog_win = newtProgressWindowOpen(52, 5, "UDEV initialization");
    start_udev(prog_win, 100);
    sleep(1);
    newtProgressWindowClose(prog_win);

    return NULL;
}

#if 0
#define UDEVD_BIN "/sbin/udevd"
#define UDEVTRIGGER_BIN "/sbin/udevtrigger"
#define UDEVSETTLE_BIN "/sbin/udevsettle"

#else
#define MDEV "/sbin/mdev"
#endif

void start_udev(newtProgressWindow prog_win, int steps)
{
    int steps_left = steps;

    PRINTF("Starting UDEV...");

    /* mount /dev */
    if (mount(NULL, "/dev", "ramfs", MS_MGC_VAL, NULL) != 0) {
        mblog("mount /dev failed: %s", strerror(errno));
        goto err0;
    }
    if (mkdir("/dev/input", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
        mblog("Create directory error: %s", strerror(errno));
        goto err0;
    }
    STEPIT(steps * 10 / 100);

    /* write hotplug */
    {
        int fd;
        fd = open("/proc/sys/kernel/hotplug", O_WRONLY);
        if (fd < 0) {
            mblog("Error writing /proc/sys/kernel/hotplug.");
            goto err0;
        }
#ifdef MDEV
        write(fd, MDEV, sizeof(MDEV)-1);
#else
       
        //write(fd, UDEV_BIN, sizeof(UDEV_BIN));
        write(fd, "\0\0\0\0", 4);
#endif
        close(fd);
    }
    STEPIT(steps * 10 / 100);

    /* start udev daemon */
    {
#ifdef MDEV
        char *argv[] = {MDEV, "-s", NULL};
#else
        char *argv[] = {UDEVD_BIN, "-d", NULL};
#endif
        if (exec_and_wait(argv) != 0) {
            mblog("Run udev failed.\n");
            goto err0;
        }
    }
    STEPIT(steps * 10 / 100);

#ifdef MDEV
#else
    /* run udevtrigger */
    {
        char *argv[] = {UDEVTRIGGER_BIN, NULL};
        if (exec_and_wait(argv) != 0) {
            mblog("Run %s failed.", UDEVTRIGGER_BIN);
            goto err0;
        }
    }
#endif
    STEPIT(steps * 40 / 100);

#ifdef MDEV
#else
    /* run udevsettle */
    {
        char *argv[] = {UDEVSETTLE_BIN, NULL};
        if (exec_and_wait(argv) != 0) {
            mblog("Run %s failed.", UDEVSETTLE_BIN);
            goto err0;
        }
    }
#endif
    STEPIT(steps * 20 / 100);

    /* post operations */
    if (symlink("/proc/self/fd", "/dev/fd") ||
        symlink("/proc/self/fd/0", "/dev/stdin") ||
        symlink("/proc/self/fd/1", "/dev/stdout") ||
        symlink("/proc/self/fd/2", "/dev/stderr") ||
        symlink("/proc/kcore", "/dev/core")) {
        mblog("Link file error: %s", strerror(errno));
        goto err0;
    }
    if (mkdir("/dev/pts", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) ||
        mkdir("/dev/shm", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
        mblog("Create directory error: %s", strerror(errno));
        goto err0;
    }
    STEPIT(steps * 10 / 100);

    /* ALL DONE */
    CONTPRINTF("[OK]");
    if (steps_left > 0)
        STEPIT(steps_left);
    return;

  err0:
    CONTPRINTF("[Failed]");
    if (steps_left > 0)
        STEPIT(steps_left);
    newtWinMessage("Error", "OK",
                   "Udev start failed, the installer may not work.");
}
#endif  /* USE_UDEV */

#if 0
void detect_video(void)
{
    struct device ** devices, ** device;
    char *driver;
    newtComponent label;
    char labelbuf[128];

    char **argv;
    char *i810fb_argv[] = {
        "/sbin/modprobe",
        "i810fb",
        "vram=2",
        "xres=800",
        "yres=600",
        "bpp=16",
        "hsync1=30",
        "hsync2=55",
        "vsync1=50",
        "vsync2=85",
        "accel=1",
        NULL};
    char *intelfb_argv[] = {
        "/sbin/modprobe",
        "intelfb",
        "mode=800x600-16@85",
        NULL};

    newtCenteredWindow(52, 3, "Probe video card");
    label = newtLabel(2, 1, "Probing video card...");
    newtRefresh();

    devices = probeDevices(CLASS_VIDEO, BUS_UNSPEC, PROBE_ALL);
    if (devices != NULL) {
        for (device = devices; *device; device++) {
            driver = (*device)->driver;
            mblog("Video card with driver '%s'.\n", driver);
            argv = NULL;
            if (strstr(driver, "Intel 810")
                || strstr(driver, "Intel 815")) {
                argv = i810fb_argv;
            } else if (strstr(driver, "Intel 830")
                       || strstr(driver, "Intel 845")
                       || strstr(driver, "Intel 852")
                       || strstr(driver, "Intel 855")
                       || strstr(driver, "Intel 865")) {
                argv = intelfb_argv;
            }
            if (argv) {
                snprintf(labelbuf,  sizeof(labelbuf), "Loading module %s...", argv[1]);
                if (do_modprobe2(argv) >= 0)
                    strcat(labelbuf, "OK.");
                else
                    strcat(labelbuf, "Failed!");
                newtLabelSetText(label, labelbuf);
                newtRefresh();
                sleep(2);
            }
            freeDevice (*device);
        }
        free(devices);
    }
    newtPopWindow();
}
#endif
